package org.ytor.core.sqlflow.builder.support;

import org.ytor.common.classmeta.ClassMetadata;
import org.ytor.common.classmeta.MethodMetadata;
import org.ytor.common.util.Colls;
import org.ytor.common.util.Strs;
import org.ytor.common.util.cache.ClassCache;
import org.ytor.common.util.id.Ids;
import org.ytor.core.sqlflow.PersistenceModel;
import org.ytor.core.sqlflow.SFunction;
import org.ytor.core.sqlflow.anno.Table;
import org.ytor.core.sqlflow.builder.AbsBuilder;
import org.ytor.core.sqlflow.builder.IOrderedParams;
import org.ytor.core.sqlflow.enums.IdType;
import org.ytor.core.sqlflow.executor.DMLResult;
import org.ytor.core.sqlflow.executor.SQLRuntime;
import org.ytor.core.sqlflow.log.SqlLogHelper;
import org.ytor.core.sqlflow.plugin.Interceptor;
import org.ytor.core.sqlflow.plugin.InterceptorManager;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * created by yangtong on 2025/8/8 10:30:54
 * <br/>
 * INSERT 构建器（兼容单条与批量）
 * 语法：
 *   new InsertBuilder<>()
 *      .into(SysUser.class)
 *      .value(entity)              // 单条
 *      .start();
 *
 *   new InsertBuilder<>()
 *      .into(SysUser.class)
 *      .values(list)               // 批量
 *      .startBatch();
 *
 *   // 也支持手工指定列值（单条）
 *   new InsertBuilder<>()
 *      .into(SysUser.class)
 *      .value("name", "Alice")
 *      .value(u -> u.getAge(), 18)
 *      .start();
 *
 * 设计要点：
 *  - 批量插入采取“固定列超集 + NULL 补齐”的策略（方案A），保证同构 SQL，充分利用 rewriteBatchedStatements。
 *  - 若实体 id 为空且 @TableId(type=ASSIGN_ID)，会为每条记录预分配雪花ID，并尽力回填到 ExecuteResult.id。
 *  - value(String/SFunction, Object) 作为“全局覆盖值”，在批量时对所有行生效（当该列在实体中为 null 时）。
 */
public class InsertBuilder<T extends PersistenceModel<T, ?>> extends AbsBuilder implements IOrderedParams {

    /**
     * 主表实体类
     */
    private Class<?> table;

    /**
     * 表名，由clazz推断出的字符串
     */
    private String tableName;

    /**
     * 新增的数据，支持单挑插入和批量插入
     */
    private final List<T> entities = new ArrayList<>();

    /** 全局覆盖的列值（手工指定的列值；批量时对所有记录生效，用于缺省补齐） */
    private final Map<String, Object> overrideCols = new LinkedHashMap<>();

    /**
     * 抽取后的固定列顺序（同构
     */
    private List<String> fixedColumns = Collections.emptyList();

    /**
     * 通过解析entities而产生的批量参数
     */
    private List<List<Object>> batchParams = Collections.emptyList();

    /**
     * 批量时，按顺序记录为每条数据预分配的ID（若无生成则为 null）
     */
    private List<String> generatedIds = Collections.emptyList();

    /**
     * 是否抽取过参数
     */
    private boolean extracted = false;


    /**
     * 指定数据插入的表
     */
    public InsertBuilder<T> into(Class<?> table) {
        this.table = table;
        // 解析表名
        this.tableName = getTableName(table);
        return this;
    }

    /**
     * 单条插入：追加一个实体
     */
    public InsertBuilder<T> value(T entity) {
        if (entity != null) this.entities.add(entity);
        return this;
    }

    /**
     * 批量插入：追加多个实体
     */
    public InsertBuilder<T> values(List<T> entities) {
        if (!Colls.isEmpty(entities)) this.entities.addAll(entities);
        return this;
    }

    /**
     * 手工指定列值（列名）—— 单条或批量的全局覆盖值
     */
    public InsertBuilder<T> value(String column, Object val) {
        if (Strs.isEmpty(column)) return this;
        overrideCols.put(column.trim(), val);
        return this;
    }

    /**
     * 手工指定列值（属性引用）—— 单条或批量的全局覆盖值
     */
    public InsertBuilder<T> value(SFunction<T, ?> column, Object val) {
        overrideCols.put(resolveColumn(column), val);
        return this;
    }

    // ------------------------------------------------------ 核心构建

    private void ensureExtracted() {
        if (extracted) return;
        if (table == null) throw new IllegalStateException("INSERT 未指定表：请先调用 into(Class)");

        if (Strs.isEmpty(this.tableName)) {
            // 回退：按类名推导
            this.tableName = Strs.toUnderline(table.getSimpleName());
        }

        // 读取 getter 元数据（稳定顺序：getXxx 按声明顺序）
        ClassMetadata<?> classMetadata = ClassCache.get(table);
        List<MethodMetadata> getters = classMetadata.getMethods(m ->
                m.getName().startsWith("get") && m.parameters().isEmpty() && m.isPublic());

        // 逐实体抽取非空列 -> 行 Map（列名->值）；并处理 ASSIGN_ID
        List<Map<String, Object>> rowMaps = new ArrayList<>();
        List<String> ids = new ArrayList<>();

        if (!entities.isEmpty()) {
            for (T e : entities) {
                Map<String, Object> row = new LinkedHashMap<>();
                // 读取所有 getter
                for (MethodMetadata getter : getters) {
                    String prop = getter.getName().substring(3);
                    String colName = Strs.toUnderline(prop);
                    try {
                        Object v = getter.invoke(e);
                        if (v != null) row.put(colName, v);
                    } catch (InvocationTargetException | IllegalAccessException ex) {
                        throw new RuntimeException("读取属性失败: " + getter.getName(), ex);
                    }
                }
                Object existedId = row.get("id");
                // 如果指定了id，就是要指定的id
                if (existedId != null) {
                    ids.add(String.valueOf(existedId));
                }
                // 否则生成一个id
                else {
                    Object id = genIdIfNecessary();
                    if (id != null) {
                        row.put("id", id);
                        ids.add(id.toString());
                    }
                }

                rowMaps.add(row);
            }
        }

        // 计算固定列超集：实体非空列 ∪ override 列（保持首次出现顺序）
        LinkedHashSet<String> cols = new LinkedHashSet<>();
        for (Map<String, Object> m : rowMaps) cols.addAll(m.keySet());
        cols.addAll(overrideCols.keySet());

        if (cols.isEmpty()) {
            if (overrideCols.isEmpty()) {
                throw new IllegalStateException("INSERT 无可插入字段（所有实体字段为 null？或未调用 value/values?）");
            } else {
                // 允许仅使用覆盖列进行插入（单条）
                cols.addAll(overrideCols.keySet());
            }
        }

        this.fixedColumns = new ArrayList<>(cols);

        // 组装 batch 参数（按固定列顺序；缺失列用 override 或 null 补齐）
        List<List<Object>> params = new ArrayList<>();
        if (!rowMaps.isEmpty()) {
            for (Map<String, Object> m : rowMaps) {
                List<Object> row = new ArrayList<>(fixedColumns.size());
                for (String c : fixedColumns) {
                    Object v = m.get(c);
                    if (v == null && overrideCols.containsKey(c)) v = overrideCols.get(c);
                    row.add(v);
                }
                params.add(row);
            }
        } else {
            // 仅覆盖列的单条插入
            List<Object> row = new ArrayList<>(fixedColumns.size());
            for (String c : fixedColumns) row.add(overrideCols.get(c));
            params.add(row);
        }

        this.batchParams = params;
        // 与 entities 对齐；仅当基于实体构造时有效
        this.generatedIds = ids;
        this.extracted = true;
    }

    @Override
    public String toSql() {
        if (tableName == null) {
            SqlLogHelper.error("INSERT 缺少表, 请检查");
            throw new IllegalStateException("INSERT 缺少表，请检查");
        }

        ensureExtracted();
        String cols = String.join(", ", fixedColumns);
        String qs = fixedColumns.stream().map(c -> "?").collect(Collectors.joining(", "));
        return "INSERT INTO " + tableName + " (" + cols + ") VALUES (" + qs + ")";
    }

    private String toInnerSql(Boolean isBatch) {
        List<Interceptor> interceptors = InterceptorManager.interceptors();
        for (Interceptor interceptor : interceptors) {
            interceptor.beforeInsertBuilder(table, tableName);
        }
        return toSql();
    }

    @Override
    public List<List<Object>> orderedParams() {
        ensureExtracted();
        return batchParams;
    }

    /**
     * 提交SQL执行
     */
    public DMLResult submit() {
        ensureExtracted();
        if (batchParams.size() != 1) {
            throw new IllegalStateException("当前包含 " + batchParams.size() + " 条记录，請使用 startBatch() 执行批量插入");
        }

        String sql = toInnerSql(false);
        List<Object> params = batchParams.getFirst();
        // INSERT执行前
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeInsert(table, tableName, sql, params);
        }

        DMLResult result = SQLRuntime.doInsert(sql, params);
        // INSERT执行后
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterInsert(table, tableName, sql, params, result);
        }

        // 回填预分配ID
        if (result.getId() == null && !generatedIds.isEmpty()) {
            String gid = generatedIds.getFirst();
            if (!Strs.isEmpty(gid)) result.setId(gid);
        }
        return result;
    }

    /**
     * 批量提交SQL执行
     */
    public List<DMLResult> submitBatch() {
        ensureExtracted();
        if (batchParams.isEmpty()) {
            throw new IllegalStateException("没有可执行的数据");
        }

        String sql = toInnerSql(true);
        // 批量INSERT执行前
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeInsertBatch(table, tableName, sql, batchParams);
        }

        List<DMLResult> results = SQLRuntime.doInsertBatch(sql, batchParams);

        // 批量INSERT执行后
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterInsertBatch(table, tableName, sql, batchParams, results);
        }

        // 回填预分配ID（驱动不返回时）
        if (!Colls.isEmpty(results) && !Colls.isEmpty(generatedIds)) {
            int n = Math.min(results.size(), generatedIds.size());
            for (int i = 0; i < n; i++) {
                DMLResult er = results.get(i);
                if (er.getId() == null) {
                    String gid = generatedIds.get(i);
                    if (!Strs.isEmpty(gid)) er.setId(gid);
                }
            }
        }
        return results;
    }

    /**
     * 通过 SFunction 解析列名（不带别名）
     */
    private String resolveColumn(SFunction<T, ?> fn) {
        try {
            SerializedLambda lambda = fn.parse();
            String method = lambda.getImplMethodName();
            if (method.startsWith("get")) method = method.substring(3);
            return Strs.toUnderline(method);
        } catch (Exception e) {
            throw new RuntimeException("字段解析失败", e);
        }
    }

    /**
     * 新增数据时，如果有必要，则生成ID
     */
    private Object genIdIfNecessary() {
        // ID类型
        IdType idType = null;
        try {
            ClassMetadata<?> classMetadata = ClassCache.get(table);
            Table tableAnno = classMetadata.getAnnotation(Table.class);
            if (tableAnno != null) {
                idType = tableAnno.idType();
            }
        } catch (Throwable ignore) {
            // 没拿到元信息也没关系
            return null;
        }

        if (idType == null)
            return null;
        else if (idType == IdType.SNOWFLAKE) {
            return Ids.snowflakeId();
        } else if (idType == IdType.UCID) {
            return Ids.ulid();
        }
        return null;
    }
}
